﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace _07_TCPClient
{
    class Program
    {
        static ManualResetEvent[] doneEvents;  // Используется для синхронизации с клиентскими потоками

        static void Main(string[] args)
        {
            
            Thread thr = new Thread(new ThreadStart(Server));thr.Start(); //запускаем параллельный поток с сервером

            Console.WriteLine("Press Enter to start client");
            Console.ReadLine();

            DateTime current = DateTime.Now;

            doneEvents = new ManualResetEvent[10]; // Задаем количество клиентов
            for (int i = 0; i < doneEvents.Length; i++)
            {
                ManualResetEvent evt = doneEvents[i] = new ManualResetEvent(false); // Устанавливаем событие в ожидание

                ThreadPool.QueueUserWorkItem(new WaitCallback((obj) => // Ставим поток в очередь выполнения
                {
                    Client(evt);
                }));
            }

            WaitHandle.WaitAll(doneEvents); // Ждем всех клиентов
            DateTime total = DateTime.Now;
            Console.WriteLine(String.Format("Time : {0} ms", total.Subtract(current).TotalMilliseconds));

            
            Console.ReadLine();
            thr.Abort();
        }

        public static void Client(ManualResetEvent evt)
        {
            try
            {
                IPEndPoint ipep = new IPEndPoint(IPAddress.Loopback, 9050);
                Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                try
                {
                    server.Connect(ipep); // Подсоединяемся к серверу
                }
                catch (SocketException e)
                {
                    Console.WriteLine("Client: Unable to connect to server.");
                    Console.WriteLine(e.ToString());

                    return;
                }


                try
                {
                    byte[] data = null;
                    int total = 0;
                    IPEndPoint server_point = (IPEndPoint)server.RemoteEndPoint;
                    IPEndPoint server_point_local = (IPEndPoint)server.LocalEndPoint;
                    Console.WriteLine("Client: Connected with {0} at port {1} remote", server_point.Address, server_point.Port);
                    Console.WriteLine("Client: Connected with {0} at port {1} local", server_point_local.Address, server_point_local.Port);
                    SendData(server, Encoding.ASCII.GetBytes("Hello world!"));
                    while ((total = ReceiveData(server, 1024, ref data)) > 0)
                    {
                        String stringData = Encoding.ASCII.GetString(data, 0, total);
                        Console.WriteLine(stringData);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine("Меня игнорируют:" + e.Message);
                }
                Console.WriteLine("Client: Disconnecting from server...");
                server.Shutdown(SocketShutdown.Both);
                server.Close();
            }
            finally
            {
                evt.Set(); //Устанавливаем событие (что бы отследить момент когда все потоки закончаться)
            }
        }

        public static void ServerInternal( Socket client) // Функция сервера, реализующая работу с клиентом
        {
            Thread.Sleep(1000);
            byte[] data = null;
            IPEndPoint newclient = (IPEndPoint)client.RemoteEndPoint;

            Console.WriteLine("Server: Connected with {0} at port {1}", newclient.Address, newclient.Port);
            ReceiveData(client, 12, ref data);
            Console.WriteLine(Encoding.ASCII.GetString(data));

            int sent = SendData(client, Encoding.ASCII.GetBytes("Welcome to my test server"));
            Console.WriteLine("Server: Disconnected from {0}", newclient.Address);

            client.Close();
        }

        public static void Server() // Функция сервера реализующая цикл ожидания клиентов
        {
            
            IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
            Socket newsock = new Socket(AddressFamily.InterNetwork,
                                        SocketType.Stream, ProtocolType.Tcp);

            newsock.Bind(ipep);
            newsock.Listen(20);
            try
            {
                int i = 0;
                do
                {
                    
                    Console.WriteLine(String.Format("Server: Waiting for a client: {0}",++i));
                    Socket client = newsock.Accept();

                    /*
                      ServerInternal(client);
                    /*/
                    ThreadPool.QueueUserWorkItem(new WaitCallback((obj) => //Запускаем обработчик клиента в параллельном потоке
                                       {
                                           ServerInternal(client);
                                       }));
                     //*/
                    
                 } while (true);
            }
            catch (Exception e)
            {
                Console.WriteLine("До свидания! :" + e.Message);
            }
            finally
            {
                newsock.Close();
            }
        }

        private static int SendData(Socket s, byte[] data)
        {
            int total = 0;
            int sent = 0;
            int dataleft = data.Length;

            while (total < data.Length)
            {
                sent = s.Send(data, total, dataleft, SocketFlags.None);
                total += sent;
                dataleft -= sent;
            }
            
            return total;
        }
        private static int ReceiveData(Socket s, int size,ref byte[] data)
        {
            int total = 0;
            int recv;
            int dataleft = size;
            data = new byte[size];

            while (total < size)
            {
                recv = s.Receive(data, total, dataleft, SocketFlags.None);
                if (recv == 0)
                {
                    return total;
                 }
                total += recv;
                dataleft -= recv;
            }
            return total;
        }

    }
}
